package com.example.gateway.filter;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

import javax.validation.constraints.NotNull;

import com.alibaba.fastjson.JSON;
import com.example.gateway.config.IgnoreUrlsConfig;
import com.example.gateway.model.HttpRequestLog;
import com.example.gateway.model.RequestObject;
import com.example.gateway.model.ResponseModel;
import com.example.gateway.respository.HttpRequestLogRepository;
import com.example.gateway.rpc.UserRpc;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Slf4j
@Component
public class AuthFilter implements GlobalFilter {
    private static final String OAUTH_TOKEN_URL="oauth/token";
    @Autowired
    private UserRpc userRpc;
    @Value("${auth.token.header}")
    private String ACCESS_TOKEN;
    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;
    @Autowired
    private HttpRequestLogRepository requestLogRepository;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String requestUrl=exchange.getRequest().getURI().toString();
        if(requestUrl.contains(OAUTH_TOKEN_URL)){
            return chain.filter(exchange);
        }
        //白名单
        var whiteUrl=false;
        if(ignoreUrlsConfig.getUrls().size()>0){
            URI uri = exchange.getRequest().getURI();
            PathMatcher pathMatcher = new AntPathMatcher();
            
            List<String> ignoreUrls = ignoreUrlsConfig.getUrls();
            
            for (String ignoreUrl : ignoreUrls) {
                if (pathMatcher.match(ignoreUrl, uri.getPath())) {
                    whiteUrl=true;
                }
            }
        }
        String token = exchange.getRequest().getHeaders().getFirst(ACCESS_TOKEN);
        if (StringUtils.isEmpty(token)&&!whiteUrl) {
            chain.filter(exchange);
            return getVoidMono(exchange, ResponseModel.error("token为空。"));
        }
        //从token中解析用户信息并设置到Header中去
        if(!StringUtils.isEmpty(token)&&!whiteUrl){
            String realToken = token.replace("Bearer ", "");
            var validateResult=userRpc.validate(realToken);
            if(!validateResult.isSuccess()){
                chain.filter(exchange);
                return getVoidMono(exchange, validateResult);
            }
            var userResult=userRpc.getUserInfo(realToken);
            if(!userResult.isSuccess()){
                chain.filter(exchange);
                return getVoidMono(exchange, userResult);
            }
            //带token的一般请求才记录请求日志
            var requestLog=new HttpRequestLog();
            var requestObj=new RequestObject();
            requestObj.setBody(JSON.toJSONString(exchange.getRequest().getBody()));
            requestObj.setHeader(JSON.toJSONString(exchange.getRequest().getHeaders()));
            requestLog.setParameters(JSON.toJSONString(requestObj));
            var reqUrl=exchange.getRequest().getURI().getPath();
            if(!StringUtils.isEmpty(exchange.getRequest().getURI().getQuery())){
                reqUrl+="?"+exchange.getRequest().getURI().getQuery();
            }
            requestLog.setUrl(reqUrl);
            requestLog.setUserName(userResult.getData().getUsername());
            requestLog.setRequestTime(LocalDateTime.now());
            requestLog.setId(0L);
            logHttpRequest(requestLog);
            var userStr=JSON.toJSONString(userResult.getData());
            log.info("AuthGlobalFilter.filter() user:{}",userStr);
            ServerHttpRequest request = exchange.getRequest().mutate().header("UserInfo", userStr).build();
            exchange = exchange.mutate().request(request).build();
        }
        
        return chain.filter(exchange);
    }
    
    /**
     * 网关抛异常
     *
     * @param body
     */
    @NotNull
    private Mono<Void> getVoidMono(ServerWebExchange serverWebExchange, ResponseModel body) {
        serverWebExchange.getResponse().setStatusCode(HttpStatus.OK);
        byte[] bytes = JSON.toJSONString(body).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = serverWebExchange.getResponse().bufferFactory().wrap(bytes);
        return serverWebExchange.getResponse().writeWith(Flux.just(buffer));
    }
    /**
     * 写http请求日志
     * @param msg
     * @param logInfo
     */
    private void logHttpRequest(HttpRequestLog logInfo){
        var executor=Executors.newSingleThreadExecutor();
        executor.execute(new Runnable(){

            @Override
            public void run() {
                requestLogRepository.save(logInfo);
            }
            
        });
        executor.shutdown();
    }
}
